﻿

(function () {
    var jXSockets = {

        xWebSocket: function (url, subprotocol) {
            var callbacks = {};
            var conn = null;
            if ('WebSocket' in window) {
                conn = new WebSocket(url, subprotocol);

            } else if ('MozWebSocket' in window) {
                conn = new MozWebSocket(url, subprotocol);

            } else {
                conn = null;
            }
            if (conn != null) {

                conn.onclose = function () {
                    dispatch('close', null);
                };
                conn.onopen = function () {
                    dispatch('open', null);
                };
                conn.onmessage = function (a) {
                    messagehandler(a);
                };
            } else { }
            function addMethod(object, name, fn) {
                var old = object[name];
                if (old) object[name] = function () {
                    if (fn.length == arguments.length) return fn.apply(this, arguments);
                    else if (typeof old == 'function') return old.apply(this, arguments);
                };
                else object[name] = fn;
            }
            addMethod(this, "bind", function (event_name, callback) {
                callbacks[event_name] = callbacks[event_name] || [];
                callbacks[event_name].push({
                    callback: callback,
                    options: null
                });
                return this;
            });
            addMethod(this, "bind", function (event_name, options, callback) {
                callbacks[event_name] = callbacks[event_name] || [];
                callbacks[event_name].push({
                    callback: callback,
                    options: options
                });
                return this;
            });
            addMethod(this, "many", function (event_name, count, callback) {
                callbacks[event_name] = callbacks[event_name] || [];
                callbacks[event_name].push({
                    callback: callback,
                    options: {
                        Counter: {
                            Messages: count,
                            Completed: function () {
                                for (var i = 0; i < callbacks[event_name].length; i++) {
                                    callbacks[event_name].splice(i, 1);
                                    break;
                                }
                            }
                        }
                    }
                });
                return this;
            });
            addMethod(this, "one", function (event_name, callback) {
                callbacks[event_name] = callbacks[event_name] || [];
                callbacks[event_name].push({
                    callback: callback,
                    options: {
                        Counter: {
                            Messages: 1,
                            Completed: function () {
                                for (var i = 0; i < callbacks[event_name].length; i++) {
                                    callbacks[event_name].splice(i, 1);
                                    //                                    break;
                                }
                            }
                        }
                    }
                });
                return this;
            });
            addMethod(this, "unbind", function (eventName) {
                for (var i = 0; i < callbacks[eventName].length; i++) {
                    callbacks[eventName].splice(i, 1);
                }
            });

            addMethod(this, "unbind", function (eventName, callback) {
                for (var i = 0; i < callbacks[eventName].length; i++) {
                    if (callbacks[eventName][i] == callback) {
                        callbacks[eventName].splice(i, 1);
                        break;
                    }
                }
            });
            addMethod(this, "trigger", function (data) {
                send(JSON.stringify(data, null, 0));
            });

            addMethod(this, "trigger", function (message, data) {
                var payload = null;
                if (typeof (data) == "function") {
                    payload = JSON.stringify(message, null, 0)
                } else {
                    var event = new jXSockets.WebSocketMessage(message);
                    event.AddJson(data);
                    payload = event.toString();
                }
                send(payload);
                if (typeof (data) == "function") {
                    if (typeof (payload) == "object") {
                        data(payload);
                    } else {
                        data(JSON.parse(payload));
                    }
                }
                return message;
            });

            addMethod(this, "trigger", function (message, data, oncompleted) {
                var event = new jXSockets.WebSocketMessage(message);
                event.AddJson(data);
                send(event.toString());
                oncompleted(event.Message);
            });

            var send = function (payload) {

                if (conn.bufferedAmount == 0) {
                    dispatch("send", {bufferedAmount: conn.bufferedAmount });
                    conn.send(payload);
                }
            }



            var messagehandler = function (message) {
                raiseEvent(message);
            };

            addMethod(this, "send", function (payload) {
                send(payload);
            });


            var dispatch = function (event_name, message) {
                var chain = callbacks[event_name];
                if (typeof chain == 'undefined') return;
                for (var i = 0; i < chain.length; i++) {
                    chain[i].callback(message);
                    var opts = chain[i].options;
                    if (opts != null) {
                        if (opts.Counter !== undefined) {
                            chain[i].options.Counter.Messages--;
                            if (chain[i].options.Counter.Messages == 0) {
                                if (typeof opts.Counter.Completed !== 'undefined') {
                                    opts.Counter.Completed();
                                }
                            }
                        }
                    }
                }
            };

            var raiseEvent = function (message) {

                var msg;
                var event_name;
                if (typeof message.data === "string") {
                    msg = JSON.parse(message.data);
                    event_name = msg.event;
                    dispatch(event_name, msg.data);
                } else {


                    dispatch("blob", message.data);
                }

            };


            //            addMethod(this, "xWebSocketMessage", function () {
            //                return new jXSockets.WebSocketMessage("HelloWorld");
            //            });

            this.WebSocketMessage = function (name, options) {
                var message = new $$.WebSocketMessage(name);
                message._trigger = this.trigger;
                message.publish = function () {
                    this._trigger(message.Message);
                };
                this.bind(name, options.subscribe);
                return message;
            };



            return this;
        },



        WebSocketMessage: function (eventName) {
            this.EventName = eventName;
            this.Message = {
                event: eventName,
                data: null
            };

            function addMethod(object, name, fn) {
                var old = object[name];
                if (old) object[name] = function () {
                    if (fn.length == arguments.length) return fn.apply(this, arguments);
                    else if (typeof old == 'function') return old.apply(this, arguments);
                };
                else object[name] = fn;
            }
            this.AddJson = function (obj) {
                this.Message.data = obj;
                return this;
            };
            this.AddJsonString = function (jsonStr) {
                this.Message.Data = JSON.parse(jsonStr);
                return this.Message;
            };
            this.toString = function toString() {
                return JSON.stringify(this.Message);
            };
            this.PayLoad = function () {
                return this.Message;
            };
            return this;
        }
    };

    if (!window.jXSockets) {
        window.jXSockets = jXSockets;
    }
    if (!window.$$) {
        window.$$ = jXSockets;
    }
})();